Conversation
|
#18 is linked to this PR, as if it fixes the issue. But if I understand correctly, the issue is not fixed, right? |
Ah, I thought I had fixed that but now I did for real. |
|
Performance compared to mut-treelist before: After: |
|
This is ready to merge, right? |
|
From my perspective yes although I was hoping for a review from @rmculpepper |
rmculpepper
left a comment
There was a problem hiding this comment.
Gvectors were previously unsynchronized and so not thread-safe. The introduction of unsafe operations makes them potentially memory-unsafe, and we need to make sure gvector operations, even used concurrently, are still memory-safe.
The gv-ensure-space! pattern helps: it means that writes to the vector have enough space, whether or not that vector is actually installed as the gvector's vec field at the end of the operation (eg, because of two racing add operations). I'm worried about the write to n afterwards; in a race, it might point past the end of vec.
I've pointed out a problem in gvector-ref if there is a concurrent call that shrinks the gvector. It also occurs if n is greater than the length of the vector. Other read operations (iteration, for example) might have similar problems, but I haven't checked them all.
If unsafe operations are used, we need to figure out and document invariants and patterns of field updates that actually preserve memory-safety.
data-lib/data/gvector.rkt
Outdated
| (define v (ensure-free-space-vec! (gvector-vec gv) (gvector-n gv) needed-free-space)) | ||
| (when v (set-gvector-vec! gv v))) | ||
|
|
||
| (define-syntax-rule (gv-ensure-space! gv n v needed-free-space) |
There was a problem hiding this comment.
I think a syntax like (define/ensure-space! (n v) gv needed-free-space) would better signal what this macro is doing.
data-lib/data/gvector.rkt
Outdated
| (raise-type-error 'gvector-ref "exact nonnegative integer" index)) | ||
| (if (< index (gvector-n gv)) | ||
| (vector-ref (gvector-vec gv) index) | ||
| (unsafe-vector*-ref (gvector-vec gv) index) |
There was a problem hiding this comment.
I think this is unsafe if a concurrent call to remove shrinks the vector. That is, if (gvector-n gv) in the previous line is fetched before the call to remove and (gvector-vec gv) is fetched afterwards, the n might be stale and the vector can be too short.
data-lib/data/gvector.rkt
Outdated
|
|
||
| ;; gvector-set! with index = |gv| is interpreted as gvector-add! | ||
| (define (gvector-set! gv index item) | ||
| (check-gvector 'gvector-set gv) |
There was a problem hiding this comment.
should be 'gvector-set!
data-lib/data/gvector.rkt
Outdated
| (gvector-add! gv item) | ||
| (vector-set! (gvector-vec gv) index item)))) | ||
| (if (unsafe-fx= index n) | ||
| (unsafe-gvector-add! gv item) |
There was a problem hiding this comment.
I don't see why unsafe-gvector-add!'s precondition (not a chaperone) is satisfied here.
* Eliminates contracts in favor of inline checks. * Specializes multiple-value code to improve single-value performance (see racket/racket#4942). * Use simpler non-recursive growth computation (taken from Rust's Vector implementation). * Avoid duplicate work in core operations. * Add #:capacity arguments. * Use unsafe operations. * Use `vector-extend` from racket/racket#4943.
- Rename gv-ensure-space! macro to define/ensure-space! with
definition-like syntax for clarity.
- Fix vec-before-n read ordering in gvector-ref and gvector-set!
to prevent memory-unsafe access during concurrent modification.
- Fix error symbol typo in gvector-set! ('gvector-set -> 'gvector-set!).
- Add impersonator? check in gvector-set! before calling
unsafe-gvector-add! to handle chaperoned gvectors correctly.
- Add stress tests for concurrent and parallel thread safety.
Based on the benchmark from PR racket#34, compares gvector operations against mutable treelists across varying vector counts and lengths.
single-value performance (see Optimizations and benchmarks for gvectors racket#4942).
(taken from Rust's Vector implementation).
vector-extendfrom Addvector-extend. racket#4943.